#!/usr/local/bin/python2.7
# encoding: utf-8
'''
NervuresDesignTool -- Outil de conception/simulation de parapentes Nervures
Classe xxx
Description :
@module : graphics.polygon
@author:      puiseux
@copyright:   2013 Nervures. All rights reserved.      
@license:    LGPL
@contact:    be@nervures.com, pierre@puiseux.name
@deffield    creation: 19 janv. 2013
'''
import sys
from utilitaires.utilitaires import trace, dist, baryCentre, alert
from PyQt4 import QtCore, QtGui, Qt
from PyQt4.Qt import SIGNAL
# from gui.graphicsbase.preferences import PolyLinePrefs
import graphics.preferences as preferences

#from preferences import PolygonPrefs, PolyLinePrefs
from graphics.common import polygonFromPoints, p2t, p2s
from graphics.node import Noeud
from model.profil import EProfil
import numpy as np
class EGraphicsProfilItem(QtGui.QGraphicsPathItem):
#     from gui.graphicsbase.common import  role
    prefs = preferences.ProfilPrefs()
    Type = QtGui.QGraphicsItem.UserType + 2

    """    
    Fonctionnalités de base d'une polyline graphique, éditable à la souris, pas de surface, non fermée.
    Pour un polygone (fermé, avec une surface => cloison, renfort...) il faut utiliser NGraphicsPolygoneItem
    Pour un profil, il faut utiliser  NGraphicsProfilItem (fonctionnalités bord d'attaque, ouverture, extrados, lissage ...)
    Attributs:
    ---------
    - self.model : un modèle NPolygon (model/basicobjects/polygon.py) qui contient un QPolygonF qui fait référence
    - self.nodes : une liste de Noeuds synchronisée avec le modele
    les opérations de base se font dans le modele.
    ********* self.model fait référence en tout (sauf graphique)*******
    le QGraphicsPathItem doit être synchronisé avec self.model par le programmeur
    Méthodes:
    --------
    - __getitem__() et __setitem__() sont surchargés pour permettre d'écrire
      >>> self[k] = (1.0, 0.0) accès au point k du polygon  (en ecriture, avec mise à jour du model et du graphique)
      >>> self[k] = un_qpointf 
      >>> x, y = self[k] retourne un tuple de coordonnées
      >>> x = self[k,0] retourne la première coordonnée du point k
    """

    def __init__(self, **dump):
        """
        parametres:
        ----------
        - model le modele par exemple un Profil, NPolygone, qui peuvent déja exister.
        - points peut être : 
            * une liste ou un tuple de (xi,yi), 
            * une liste ou un tuple de QPointF, 
            * un np.ndarray, de shape (n,2), p.ex. [(x1,y1), (x2,y2), ...].
                si points est de shape (N,d) avec d>2, seules les coordonnées (i,0) et (i,1) 0<=i<N sont utilisées
        - parent ou gparentitem ou parentitem au choix: le QGraphicsItem parent (attention, ca n'est pas le QWidget parent) 
                    dans la doc Qt4 il s'agit du parentItem()
        - prefs : les préférences graphiques, une des classes dans graphicsbase.preferences.py (PolyLinePrefs, EllipsePrefs, ...)
            détermine l'aspect graphique de la polyline
        """
        #ON récupere (et on supprime de dump) les data graphiques
        if dump.has_key('parent') :
            parent = dump.pop('parent')
        elif dump.has_key('gparentitem') : 
            parent = dump.pop('gparentitem')
        elif dump.has_key('parentitem') : 
            parent = dump.pop('parentitem')
        else :
            parent = None
        super(EGraphicsProfilItem, self).__init__(parent)
        
        self.editor = dump.pop('editor')#Obligatoire, self ne peut pas exister seul, il doit s'inserer dans un editeur

        try :
            self.setPos(dump.pop('posinit'))
        except KeyError : 
            pass
        # On passe au modele les données "lourdes" (e.g. points=...)
        try : 
            self.model = dump.pop('model')#ca semble fonctionner
        except KeyError :  
            self.model = EProfil(**dump)

        self._setNodes(self.model.points)
        try :
            self.basenodeindex = dump.pop('basenodeindex')
        except KeyError : 
            self.basenodeindex = 5
        self._setPath(self.model.qpolygon)
        self.setCursor(QtCore.Qt.OpenHandCursor)
#         self.setAcceptHoverEvents(True)
        self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, False)
        self.setFlag(QtGui.QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
#         self.setAcceptDrops(True)
#         self.setDragEnabled(True)
        self.setSelected(False)
        self.setZValue(-1)
        self.createActions()
#         self.setFlag(QtGui.QGraphicsItem.ItemClipsToShape)
        self.setToolTip(self.__str__())
        '''Transformation initiale'''
        self.angle = 0.0
        self.shearx = self.sheary = 0.0
        self.homx = self.homy = 1.0
        self.stepx, self.stepy = self.prefs.DX_SCALE, self.prefs.DY_SCALE
        self.dx, self.dy = self.prefs.DX_MOVE, self.prefs.DY_MOVE
        self.graphicsstate = {'numerotation' : False, 'showpoints':False}
        
    def symetrie(self, axe):
        self.model.symetriser(axe)
        self.prepareGeometryChange()
        self._setPath(self.model.qpolygon)# le polyline graphique
        self._setNodes(self.points)# Faire suivre les noeuds graphiques
        self.update()
    
    def translate(self, (x,y)):
        '''On translate le point de base en (x,y)'''
        x0, y0 = self.basenode.x(), self.basenode.y()
        trace(self, 'x0,y0',x0,y0)
        self.model.hardTranslate((x-x0, y-y0))
        self.prepareGeometryChange()
        self._setPath(self.model.qpolygon)# le polyline graphique
        self._setNodes(self.points)# Faire suivre les noeuds graphiques
        self.update()
        
    def fantomiser(self, value=True):
        '''Rendre un polyligne fantome (superposé)  ou editable'''
#         self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, not value)
        self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, False)
        self.setFlag(QtGui.QGraphicsItem.ItemSendsGeometryChanges, not value)
        self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, not value)
        self.setSelected(not value)
        self.afficherPoints(not value)
#         for point in self.childItems() : 
#             point.setVisible(not value)
        self.setZValue(-1)
#         self.show()
    
    def edit(self):
        '''Edition des points dans un tableur'''
        alert(self, 'todo')
        
    def shouldBeClosed(self):
        if len(self.nodes)  <= 1 : 
            return False
        return dist(self.nodes[0], self.nodes[-1]) < 1.0e-8
        
    def ouvrir(self):
        '''
        Polyligne fermé => polyligne ouvert
        => marche pas tres bien...
        '''
#         trace(self)
        if len(self.nodes)  <= 1 or not self.isClosed() : 
            trace(self, 'rien')
            return
        trace(self, 'fermé?', self.isClosed(), 'nbnoeuds avant suppression', len(self.nodes), 'taille polygon', self.model.qpolygon.size())
        trace(self, 'avant', self.model.points)
        self.nodes[-1].setParentItem(self.nodes[0].parentItem())
        self.removeNode(self.nodes[-1])
        trace(self, 'apres', self.model.points)
        trace(self, 'fermé?', self.isClosed(), 'nbnoeuds après suppression', len(self.nodes), 'taille polygon', self.model.qpolygon.size())
#         self.nodes[-1].setPos(self.nodes[0].pos())
#         self.nodes[-1].setFlag(QtGui.QGraphicsItem.ItemStacksBehindParent, False)
        
    def close(self, value=True):
        '''
        - Refermer un polyline pour en faire un polygone
        - Ouvrir un polygone pour en faire un polyline
        => marche pas tres bien...
        '''
#         trace(self, value)
        if not value : 
            self.ouvrir()
        if len(self.nodes)  <= 1 or self.isClosed() : 
            return
        begin, end = self.nodes[0], self.nodes[-1]
        if dist(begin, end) > 1.0e-8 : #on rajoute le noeud de fermeture
            self.appendNode(begin.pos())
        self.nodes[-1].setParentItem(self.nodes[0])
        self.nodes[-1].setFlag(QtGui.QGraphicsItem.ItemStacksBehindParent)
    fermer = close
    
    def isClosed(self):
        if len(self.nodes)  == 0 : return None
        return self.nodes[0].isAncestorOf(self.nodes[-1])
        
    def _setPath(self, qpol):
        path = QtGui.QPainterPath()
        path.addPolygon(qpol)
        self.setPath(path)

    def __len__(self):
        return len(self.model)
    
    def type(self):
        return self.Type
    
    def _getInfo(self):
        n = type(self).__name__
        dinfos = ['id(self) = %d'%id(self)]
        minfos = self.model.info
        ginfos = [
                  u"\n<%s>"%n,#+(len(n)+2)*'=',
                  u"Nb nœuds=%d"%len(self.nodes),
#                   u"Position=(%.2g,%.2g)"%(self.x(), self.y()),
#                   u"Position=(%.2g,%.2g)"%(self.pos().x(), self.pos().y()),
#                   u"scene Position=(%.2g,%.2g)\n"%(self.scenePos().x(), self.scenePos().y()),
                 ]
        return dinfos+minfos+ginfos
    info = property(_getInfo)
    
    def __str__(self):
        return ' - '.join(self.info) 
    
    def __setitem__(self, key, value):
        self.model[key] = value
#         poly = self.model.qpolygon
#         self.polyline = poly
    
    def __getitem__(self, key):
        return self.model[key]
    '''Modifs locales : 
    - modifications dans le modele
    - la liste des noeuds n'est pas reconstruite, mais modifiée localement au cas par cas
    - le self.model.qpolygon est affecté comme nouveau path  
    - appel à self.update() ???
    '''    
    def appendNode(self, pos):
#         if self.actionVerrouiller.isChecked() : return
        self.model.appendPoint(pos)
        #voir http://www.exaflop.org/docs/cgafaq/cga1.html#Subject%201.02:%20How%20do%20I%20find%20the%20distance%20from%20a%20point%20to%20a%20line?
        #Dans ce qui suit : ne rien toucher, ca doit se faire dans cet ordre-là sinon ca marche pas...
#         self._setPath(self.model.qpolygon) je change d'ordre...
        noeud = Noeud(pos, self)
        self.nodes.append(noeud)
        self.prepareGeometryChange()
        self._setPath(self.model.qpolygon) #je change d'ordre...
        self.update()
        
    def insertNode(self, pos=None):
        """
        Parametres:
        -----------
        - pos = QPointF ou (x,y) ou [x,y]
        Si le polyline n'est pas vérouillé, insertion d'un noeud dans le segment le plus proche de pos.
        """
#        trace(self)
#         if self.actionVerrouiller.isChecked() : 
#             return
        if pos is None : 
            pos = self.actionInsertNode.data().toPointF()
            pos = self.mapFromScene(pos)
        i = self.model.insertPoint(pos) #i est l'indice d'insertion dans modele
        #voir http://www.exaflop.org/docs/cgafaq/cga1.html#Subject%201.02:%20How%20do%20I%20find%20the%20distance%20from%20a%20point%20to%20a%20line?
        #Dans ce qui suit : ne rien toucher, ca doit se faire dans cet ordre-là sinon ca marche pas...
        self.prepareGeometryChange()
        self._setPath(self.model.qpolygon)#ne pas oublier
        noeud = Noeud(pos, self)
        self.nodes.insert(i+1, noeud)
        self.update()#Ca n'est pas à self de faire ça??
    
    def removeNode(self, node):
        """
        Pour enlever un noeud, le polygone doit :
        - supprimer le point correspondant du polygone via le modele
        - reaffecter le polygone (_setPath()) 
        - supprimer le noeud de la liste
        - le supprimer de la scene
        """
#         trace(self, 'node.number', node.number)
        toshut = False
        if node == self.nodes[0] and self.isClosed():
            toshut = True
            self.removeNode(self.nodes[-1])
            
        nodenumber = self.nodes.index(node)
#         trace(self, 'self.nodes.index(node)', nodenumber)
        self.model.removePoint(nodenumber)
        self.prepareGeometryChange()
        self._setPath(self.model.qpolygon)#NE PAS OUBLIER !!!
        self.nodes.remove(node)#suppression du noeud dans la liste Python
#         node.setParentItem(None)
        self.scene().removeItem(node)
#         trace(self, 'toshut', toshut)
        try : 
            if toshut : self.close()
            self.update()
        except IndexError as msg : 
            trace(self, msg)
            pass#ya plus de polygon
    
    def moveNode(self, node):
        u"""
        le nœud 'node'  a été déplacé, mise à jour du polygone et du modele.
        """
        try :  self.nodes
        except AttributeError as msg: return#a la creation, nodes n'existe pas encore
        try : nodenumber = self.nodes.index(node)
        except ValueError as msg :return
#         trace(self, len(self))
        self.prepareGeometryChange()
#         trace(self, 'nodenumber, self.isClosed()', nodenumber, self.isClosed())
        self[nodenumber] = node.pos()
        if self.isClosed() and nodenumber == 0  :
            self[len(self)-1] = node.pos()
        self._setPath(self.model.qpolygon)#NE PAS OUBLIER !!!
        self.update()
    
    '''Transformations globales, 
    - on effectue la transformation globale sur le modele, puis
    - on reconstruit toute la liste des noeuds avec _setNodes()
    '''    
    def _setNodes(self, points):
#        trace(self)
        '''Remplir une liste de Noeuds pour affichage.
        Il faut dimensionner les nodes.rectf, sinon on a des pbs pour le recadrage.
        Par défaut, le noeud 0 prend le statut de noeud de base.
        '''
        try : 
            self.nodes
            for node in self.nodes : 
                node.setParentItem(None)
        except AttributeError : pass # self.nodes n'existe pas encore
        self.nodes = [Noeud(point, self, 'fantome') for point in points]
#         trace(self, self.nodes)
    
    def rotate(self, centre=None):    
        if centre is None : 
            centre = self.center
        self.setTransformOriginPoint(*centre)
        self.setRotation(self.angle)
    
    def hardScale(self, echelle=None, center=None):
        if echelle is None : 
            echelle = (self.homx, self.homy)
        self.model.hardScale(echelle, center)
        self._setPath(self.model.qpolygon)# le polyline graphique
        self._setNodes(self.points)# Faire suivre les noeuds graphiques
#         for k, node in enumerate(self.nodes) :
#             node.setPos(QtCore.QPointF(*self[k]))
        self.update()
    
    def hardRotate(self, angle, center=None):
        if center is None : 
            center = self.center
        self.model.hardRotate(angle, center)
        self._setPath(self.model.qpolygon)# le polyline graphique
        self._setNodes(self.points)# Faire suivre les noeuds graphiques
        self.update()
        
    def validate(self):
        '''Toutes les modifs sont faites dans le modèle, SAUF les rotations... c'est comme ca'''
        if np.abs(self.angle) >= 1.0e-5 : 
            self.hardRotate(self.angle)
        self._setPath(self.model.qpolygon)# le polyline graphique
        self._setNodes(self.points)# Faire suivre les noeuds graphiques
        
#         self.update()#c'est fait dans hardRotate
    
    def createActions(self):
        u"""
        Actions spécifiques au polyline graphique
        - numéroter
        - recadrer (la scène sur le polygon)
        - inserer un nœud
        - supprimer un nœud => scene
        On ne peut pas affecter self comme parent aux actions, car self n'est pas un QObjet 
            => self.scene() prend possession des actions définies ici
        """
        ''' Insérer un noeud '''
        self.actionInsertNode = QtGui.QAction(u"Insérer un nœud", self.scene())
        QtCore.QObject.connect(self.actionInsertNode, SIGNAL("triggered()"), self.insertNode)
        '''Supprimer un noeud est géré par la scène, c'est l'actionSuppr, générique'''
        ''' Vérouiller '''
        
        ''' Refermer un polyline '''
        self.actionFermer = QtGui.QAction(u"Fermer", self.scene())
        self.actionFermer.setCheckable(True)
        QtCore.QObject.connect(self.actionFermer, SIGNAL("triggered(bool)"), self.fermer)
        ''' Numéroter '''
        self.actionNumeroter = QtGui.QAction(u"Numéroter", self.scene())
        self.actionNumeroter.setCheckable(True)
        self.actionNumeroter.setShortcut(QtCore.Qt.Key_N)
        QtCore.QObject.connect(self.actionNumeroter, SIGNAL("triggered(bool)"), self.numeroter)
        '''Afficher les points'''
        self.actionAfficherPoints = QtGui.QAction(u"Afficher les points", self.scene())
        self.actionAfficherPoints.setCheckable(True)
        self.actionAfficherPoints.setShortcut(QtCore.Qt.Key_S)
        QtCore.QObject.connect(self.actionAfficherPoints, SIGNAL("triggered(bool)"), self.afficherPoints)
           
        ''' Liste actions spécifiques polyline'''
        self.actions = [
                        self.actionNumeroter, 
                        self.actionAfficherPoints,
#                         self.actionFermer, 
#                         self.actionInsertNode, 
#                         self.actionVerrouiller, 
                        ]
    
    def afficherPoints(self, value):
        '''Afficher ou masquer les points, sauf noeud de base, toujours affiché'''
        self.graphicsstate['showpoints'] = value
        for node in self.nodes : 
#             if node.role == 'base' : 
#                 node.setVisible(True)
#             else : 
            node.setVisible(value)
        self.basenode.setVisible(True)

    def _setBaseNodeIndex(self, index):
        oldbasenode = self.basenode
        trace(self, index)
        self.model.basenodeindex = index
        trace(self, self.basenodeindex, self.model.basenodeindex)
        self.nodes[index].role = 'base'
        if oldbasenode is not None : 
            oldbasenode.role = 'fantome'
        
    def _getBaseNodeIndex(self):
        return self.model.basenodeindex
    basenodeindex = property(_getBaseNodeIndex, _setBaseNodeIndex)

    def setBaseNode(self, bnode):
        '''
        Appelé par Noeud.setBaseNode(), 
        => NON, on passe par la voie hiérarchique, i.e. 
        GuiProfilEditor.definirPointDeBase() et les autres demandent à self s'il veut bien avoir l'amabilité de changer de point de base.
        self ordonne alors au noeud de changer de statut.
        '''
#         alert(self, 'changement noeud de base')
        self.basenodeindex = self.nodes.index(bnode)
    
    def _getBaseNode(self):
        try :
            trace(self, self.basenodeindex) 
            return self.nodes[self.basenodeindex]
        except TypeError : return None#il n'y a pas encore de noeud de base
    basenode = property(_getBaseNode)#, _setBaseNode)
    
#     def itemChange(self, change, value):
#         """
#         ATTENTION, ne surtout pas oublier le RETURN dans 
#         return super(Noeud, self).itemChange(change, value)
#         """
#         if change == QtGui.QGraphicsItem.ItemPositionHasChanged:
# #            trace(self, 'id(self) = %d'%id(self), type(self).__name__)
#             trace(self, 'ItemPositionHasChanged',)
#         return super(EGraphicsProfilItem, self).itemChange(change, value)
#          
    def numeroter(self, value):
        '''
        Afficher ou supprimer les numéros de point
        
        B1 et B2 sont deux plots à 2 etats (1/0).
        Les deux états doivent être initialement synchronisé : B1.valeur==B2.valeur
        
        **** LES DEUX envoient un signal à chaque changement d'état (toggle(bool)) ****
                                    
        on connecte ces deux signaux a traitement(valeur) ou valeur est la valeur portée par B1 et B2
        
        def traitement(valeur) :
            # 1. Synchroniser
            if B1.valeur != B2.valeur : 
                if sender == B1 :
                    B2.valeur = B1.valeur # => B2 va envoyer le signal toggle(valeur)
                    return
                elif sender == B2 :
                    B1.valeur = B2.valeur # => B1 idem
                    return
            # 2. Traitement : ici B1.valeur et B2.valeur sont égaux
            if valeur : blabla
            else: bloblo
        '''
        self.graphicsstate['numerotation'] = value
        for node in self.nodes : 
            node.hideNumber()
        if value :
            for k, node in enumerate(self.nodes) :
                if k%2==0 : node.showNumber()
        else :
            for k, node in enumerate(self.nodes) :
                if k%2==0 : node.hideNumber()
        
    
    def update(self, sender=None):
        self.prepareGeometryChange()
        for node in self.nodes :
            node.update()
        self.setToolTip(self.__str__())
#         self.numeroter(self.actionNumeroter.isChecked())
        self.editor.setGraphicsState(self.graphicsstate)
        self.numeroter(self.graphicsstate['numerotation'])
        self.afficherPoints(self.graphicsstate['showpoints'])
#         if not value : 
#             self.actionNumeroter.setChecked(False)
#         
        return super(EGraphicsProfilItem, self).update()
    
    def keyPressEvent(self, event):
        key = event.key()
#         trace(self, key)
#         modifier = event.modifiers() TODO: ca marche pas....
#         trace(self, modifier)
#         if modifier == QtCore.Qt.ControlModifier : 
#             trace(self, 'control')
        '''homothétie'''
        modifier = event.modifiers()
        trace(self, modifier)
        if key in (QtCore.Qt.Key_Left,) :#marche pas ... and modifier == QtCore.Qt.ControlModifier :
            if modifier == QtCore.Qt.ControlModifier :
                self.hardScale((1.0-self.stepx, 1.0))
            else : 
                self.moveBy(-self.dx, 0.0)
        elif key in (QtCore.Qt.Key_Right,) :
            if modifier == QtCore.Qt.ControlModifier :
                self.hardScale((1.0+self.stepx, 1.0))
            else : 
                self.moveBy(self.dx, 0.0)
        elif key in (QtCore.Qt.Key_Up, ) :
            if modifier == QtCore.Qt.ControlModifier :
                self.hardScale((1.0, 1.0+self.stepy))
            else : 
                self.moveBy(0.0, self.dy)
        elif key in (QtCore.Qt.Key_Down, ) :
            if modifier == QtCore.Qt.ControlModifier :
                self.hardScale((1.0, 1.0-self.stepy))
            else : 
                self.moveBy(0.0, -self.dy)
#         elif modifier == QtCore.Qt.NoModifier :
#             '''déplacement'''
#             if key in (QtCore.Qt.Key_Left,):
#                 self.moveBy(-self.stepx*self.width, 0.0)
#             elif key in (QtCore.Qt.Key_Right,) :
#                 self.moveBy(self.stepx*self.width, 0.0)
#             elif key in (QtCore.Qt.Key_Up, ) :
#                 self.moveBy(0.0, self.stepy*self.width)
#             elif key in (QtCore.Qt.Key_Down, ) :
#                 self.moveBy(0.0, self.stepy*self.width)
#         elif modifier == QtCore.Qt.ShiftModifier :
#             trace(self, 'todo:')
        return super(EGraphicsProfilItem, self).keyPressEvent(event)
        
     
    def paint(self, painter, options, widget):
        if self.isSelected() :
            self.setPen(self.prefs.SELECTED_PEN)
            self.setBrush(self.prefs.SELECTED_BRUSH)
            painter.drawRect(self.boundingRect())
        else :
            self.setPen(self.prefs.PEN)
            self.setBrush(self.prefs.BRUSH)
            painter.drawRect(self.boundingRect())
        super(EGraphicsProfilItem, self).paint(painter, options, widget)

    def _getName(self):
        return self.model.name
    def _setName(self, name):
        self.model.name = name
    name = property(_getName, _setName)
    def _getPoints(self):
        return self.model.points
    points = property(_getPoints)
    
    def toolTip(self):
        return str(self)
#     def applyTransformations(self) :
#         '''Appliquer les transformations au modele.Inutilisé'''
#         self.updateTransform()
#         st = self.sceneTransform()
#         qpol = st.map(self.model.qpolygon)
#         self.model.qpolygon = qpol
#         path = QtGui.QPainterPath()
#         path.addPolygon(qpol)
#         self.setPath(path)
        
    def _getCenter(self):
        return p2t(self.boundingRect().center())
#         '''isoBayrcentre'''
#         return self.model.center
    center = property(_getCenter)
#    def _getScene(self):
#        return self.scene()
#    def _setScene(self, scene):
#        self.setScene(scene)
#    scene = property(_getScene, _setScene)

class EGraphicsGabaritItem(EGraphicsProfilItem):
    '''comme un profil, mais fermé'''
    prefs = preferences.ProfilPrefs()
    Type = QtGui.QGraphicsItem.UserType + 3
    def __init__(self, **dump):
        super(EGraphicsGabaritItem, self).__init__(**dump)
        self.close(True)

##########################################################                     
##########################################################                     
##########################################################                     
##########################################################                     
##########################################################                     
if __name__ == "__main__":
    from config import DATA_DIR
    from utilitaires.path import Path
    from utilitaires.utilitaires import my2dPlot
    from scene import EGraphicsScene
    from view import EGraphicsView
    app = QtGui.QApplication(sys.argv)
    
    sys.exit(app.exec_())
        
        
